package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

func main() {
	var wg sync.WaitGroup

	// wg.Add(3)
	// ctx, stop := context.WithCancel(context.Background())
	// go func() {
	// 	defer wg.Done()
	// 	watchDog(ctx, "【监控狗1】")
	// }()
	// go func() {
	// 	defer wg.Done()
	// 	watchDog(ctx, "【监控狗2】")
	// }()
	// go func() {
	// 	defer wg.Done()
	// 	watchDog(ctx, "【监控狗3】")
	// }()
	// time.Sleep(5 * time.Second)
	// stop() // 停止指令
	// wg.Wait()

	// Context传值

	// ctx := context.Background()
	// valCtx := context.WithValue(ctx, "userId", 2)
	// go func() {
	// 	defer wg.Done()
	// 	getUser(valCtx)
	// }()
	// wg.Wait()

	wg.Add(3)
	ctx := context.Background()
	test01(ctx)
}

// 监控狗
func watchDog(ctx context.Context, name string) {
	// 开启for select循环，一直后台监控
	for {
		select {
		case <-ctx.Done():
			fmt.Println(name, "停止指令收到，马上停止")
			return
		default:
			fmt.Println(name, "正在监控......")
		}
		time.Sleep(1 * time.Second)
	}
}

// context 传值
func getUser(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			fmt.Println("【获取用户】", "协程退出")
			return
		default:
			userId := ctx.Value("userId")
			fmt.Println("【获取用户】", "用户ID为：", userId)
			time.Sleep(1 * time.Second)
		}
	}
}

type test01String string
type test02String string

func test01(ctx context.Context) {
	// context实例
	ctx01 := context.WithValue(ctx, test01String("test01"), "hello1")
	test02(ctx01)
}

func test02(ctx context.Context) {
	// context实例
	ctx02 := context.WithValue(ctx, test02String("test02"), "hello2")
	test03(ctx02)
}

func test03(ctx context.Context) {
	fmt.Println(ctx.Value(test01String("test01")))
	fmt.Println(ctx.Value(test02String("test02")))
}

/*
Context 使用原则
	Context 是一种非常好的工具，使用它可以很方便地控制取消多个协程。在 Go 语言标准库中也使用了它们，比如 net/http 中使用 Context 取消网络的请求。

	要更好地使用 Context，有一些使用原则需要尽可能地遵守。

	Context 不要放在结构体中，要以参数的方式传递。

	Context 作为函数的参数时，要放在第一位，也就是第一个参数。

	要使用 context.Background 函数生成根节点的 Context，也就是最顶层的 Context。

	Context 传值要传递必须的值，而且要尽可能地少，不要什么都传。

	Context 多协程安全，可以在多个协程中放心使用
*/